@page "/HttpTestClient"
@using System.Collections.Immutable
@using System.Text.Json
@using ArcaneLibs.Blazor.Components
@using ArcaneLibs.Extensions
@using Spacebar.AdminAPI.TestClient.Classes.OpenAPI
@using Spacebar.AdminAPI.TestClient.Pages.HttpTestClientParts
@using Spacebar.AdminAPI.TestClient.Services
@inject Config Config
<h3>HttpTestClient</h3>

@if (OpenApiSchema is not null) {
    <p>Got OpenAPI schema with @OpenApiSchema.Paths.Count paths.</p>
    <span>Server: </span>

    var currentIndex = OpenApiSchema.Servers.IndexOf(Server!);
    <InputSelect TValue="int" Value="@currentIndex" ValueExpression="@(() => currentIndex)" ValueChanged="@SetCurrentServer">
        @for (var index = 0; index < OpenApiSchema.Servers.Count; index++) {
            var server = OpenApiSchema.Servers[index];
            var serverOptionName = $"{server.Description} ({server.Url})";
            <option value="@index">@serverOptionName</option>
        }
    </InputSelect>
    <br/>
    <br/>
    <span>Path: </span>

    <InputSelect @bind-Value="_methodKey">
        <option>-- select a method --</option>
        @foreach (var method in OpenApiSchema.Paths.SelectMany(x => x.Value.GetAvailableMethods()).Distinct()) {
            <option value="@method">@method</option>
        }
    </InputSelect>

    @if (!string.IsNullOrWhiteSpace(_methodKey)) {
        <InputSelect @bind-Value="_pathKey">
            <option>-- select a path --</option>
            @foreach (var path in OpenApiSchema.Paths.Where(x => x.Value.HasMethod(_methodKey!)).OrderBy(x => x.Key)) {
                <option value="@path.Key">@path.Key</option>
            }
        </InputSelect>
        <br/>
    }

    if (Operation != null) {
        if (!string.IsNullOrWhiteSpace(Operation.Description)) {
            <p>@Operation.Description</p>
        }
    }

    <details>
        <summary>@AllKnownPathParameters.Count known path parameters</summary>
        @foreach (var (param, value) in AllKnownPathParameters) {
            var _key = param;
            // if (Operation?.Parameters?.Any(x => x.Name == param.Name && x.In == param.In) ?? false)
            // continue;
            <OpenAPIParameterDescription Parameter="@param"/>
            <br/>
        }
    </details>
    <details>
        <summary>@AllKnownQueryParameters.Count known query parameters</summary>
        @foreach (var (param, value) in AllKnownQueryParameters) {
            var _key = param;
            // if (Operation?.Parameters?.Any(x => x.Name == param.Name && x.In == param.In) ?? false)
            // continue;
            <OpenAPIParameterDescription Parameter="@param"/>
            <br/>
        }
    </details>
    
    @if (Operation != null) {
        if (Operation.Parameters?.Any() ?? false) {
            var pathParams = Operation.Parameters.Where(x => x.In == "path").ToList();
            if (pathParams.Any()) {
                <b>Path parameters</b>
                <br/>
                foreach (var key in pathParams) {
                    <span>Path parameter </span>
                    <OpenAPIParameterDescription Parameter="@key"/>
                    <br/>
                }
            }

            var queryParams = Operation.Parameters.Except(pathParams).Where(x => x.In == "query").ToList();
            if (queryParams.Any()) {
                <b>Query parameters</b>
                <br/>
                foreach (var key in queryParams) {
                    <span>Query parameter </span>
                    <OpenAPIParameterDescription Parameter="@key"/>
                    <br/>
                }
            }

            var otherParams = Operation.Parameters.Except(pathParams).Except(queryParams).ToList();
            if (otherParams.Any()) {
                <b>Other parameters</b>
                <br/>
                foreach (var key in otherParams) {
                    <span>Other parameter </span>
                    <OpenAPIParameterDescription Parameter="@key"/>
                    <br/>
                }
            }
        }

        if(Operation.RequestBody is not null) {
            <b>Request body</b>
            <br/>
            <span title="@Operation.RequestBody.ToJson()">@Operation.RequestBody.Content.ApplicationJson?.Schema.ToJson()</span>
        }
    }

    <LinkButton OnClickAsync="@Execute">Execute</LinkButton>
    <pre>@ResultContent</pre>
}

@code {
    private string? _pathKey;
    private string? _methodKey;

    private OpenApiSchema? OpenApiSchema { get; set; }
    private OpenApiServer? Server { get; set; }
    private Dictionary<OpenApiPath.OpenApiOperation.OpenApiParameter, string> AllKnownPathParameters { get; set; } = [];
    private Dictionary<OpenApiPath.OpenApiOperation.OpenApiParameter, string> AllKnownQueryParameters { get; set; } = [];

    private OpenApiPath? Path => string.IsNullOrWhiteSpace(_pathKey) ? null : OpenApiSchema?.Paths.GetValueOrDefault(_pathKey);
    private OpenApiPath.OpenApiOperation? Operation => Path is null || string.IsNullOrWhiteSpace(_methodKey) ? null : Path.GetOperation(_methodKey);

    private string? ResultContent { get; set; }
    private readonly StreamingHttpClient _httpClient = new();

    protected override async Task OnInitializedAsync() {
        _httpClient.DefaultRequestHeaders.Authorization = new("Bearer", Config.AccessToken);

        OpenApiSchema = await _httpClient.GetFromJsonAsync<OpenApiSchema>($"{Config.ApiUrl}/_spacebar/api/openapi.json");
        OpenApiSchema!.Servers.Insert(0, Server = new() {
            Description = "Current server (config)",
            Url = Config.ApiUrl + "/api/v9"
        });
        SetCurrentServer(0);

        AllKnownPathParameters = OpenApiSchema.Paths.Values
            .SelectMany(x => x.GetAvailableMethods().Select(y => x.GetOperation(y)!.Parameters ?? []))
            .SelectMany(x => x)
            .Where(x => x.In == "path")
            .DistinctBy(x => x.ToJson())
            .OrderBy(x => x.Name)
            .ToDictionary(x => x, _ => "");
        
        AllKnownQueryParameters = OpenApiSchema.Paths.Values
            .SelectMany(x => x.GetAvailableMethods().Select(y => x.GetOperation(y)!.Parameters ?? []))
            .SelectMany(x => x)
            .Where(x => x.In == "query")
            .DistinctBy(x => x.ToJson())
            .OrderBy(x => x.Name)
            .ToDictionary(x => x, _ => "");
    }

    protected override bool ShouldRender() {
        if (string.IsNullOrWhiteSpace(_methodKey))
            _pathKey = null;
        return base.ShouldRender();
    }

    private void SetCurrentServer(int index) {
        Server = OpenApiSchema!.Servers[index];
        _httpClient.BaseAddress = new Uri(Server.Url);
        StateHasChanged();
    }

    private async Task Execute() {
        var url = _pathKey!.TrimStart('/');
        if (Operation?.Parameters?.Any(x => x.In == "path") ?? false) {
            foreach (var param in Operation.Parameters.Where(x => x.In == "path")) {
                if (!AllKnownPathParameters.TryGetValue(param, out var value) || string.IsNullOrWhiteSpace(value))
                    throw new Exception($"Path parameter {param.Name} not set");
                url = url.Replace($"{{{param.Name}}}", value!);
            }
        }

        var request = new HttpRequestMessage(new HttpMethod(_methodKey!), url);
        try {
            var response = await _httpClient.SendAsync(request);
            ResultContent = response.Content.GetType().Name + "\n" + response.Content switch {
                { Headers: { ContentType: { MediaType: "application/json" } } } => (await response.Content.ReadFromJsonAsync<JsonElement>()).ToJson(true),
                _ => await response.Content.ReadAsStringAsync()
            };
        }
        catch (Exception ex) {
            ResultContent = ex.ToString();
        }

        StateHasChanged();
    }

}
